/**
 * The MIT License
 *
 * Copyright (c) 2010 Benjamin Klum
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

package org.helgoboss.maven_obr_plugin;


import java.io.File;
import java.net.URI;
import java.util.Iterator;
import java.util.List;

import org.apache.felix.bundleplugin.BundleInfo;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.factory.ArtifactFactory;
import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.artifact.resolver.ArtifactCollector;
import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
import org.apache.maven.artifact.resolver.ArtifactResolutionException;
import org.apache.maven.artifact.resolver.ArtifactResolver;
import org.apache.maven.artifact.versioning.VersionRange;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.project.MavenProject;
import org.apache.maven.shared.dependency.tree.DependencyNode;
import org.apache.maven.shared.dependency.tree.DependencyTreeBuilder;
import org.apache.maven.shared.dependency.tree.DependencyTreeBuilderException;
import org.apache.felix.obrplugin.*;


/**
 * 
 * 
 * @goal install-dependencies
 */
public final class  ObrInstallDependencies extends AbstractMojo
{
    private static final String LS = System.getProperty( "line.separator" );
    private static final int DEPTH = Integer.MAX_VALUE;
    
    
    /**
     * OBR Repository.
     * 
     * @parameter expression="${obrRepository}"
     */
    private String obrRepository;


    /**
     * Local Repository.
     * 
     * @parameter expression="${localRepository}"
     * @required
     * @readonly
     */
    private ArtifactRepository localRepository;
    

    /**
     * Optional XML file describing additional requirements and capabilities.
     * 
     * @parameter expression="${obrXml}"
     */
    protected String obrXml;

    /**
     * Remote repositories.
     * 
     * @parameter expression="${project.remoteArtifactRepositories}"
     * @required
     * @readonly
     */
    private List remoteRepositories;

    /**
     * The Maven project.
     * 
     * @parameter expression="${project}"
     * @required
     * @readonly
     */
    private MavenProject project;

    

    /**
     * Ignore missing artifacts that are not required by current project but are required by the
     * transitive dependencies.
     * 
     * @parameter
     */
    private boolean ignoreMissingArtifacts;

    

    /**
     * @component
     */
    private DependencyTreeBuilder m_dependencyTreeBuilder;

    /**
     * @component
     */
    private ArtifactFactory m_factory;

    /**
     * @component
     */
    private ArtifactMetadataSource m_artifactMetadataSource;

    /**
     * @component
     */
    private ArtifactCollector m_collector;
    

    /**
     * Artifact resolver, needed to download jars.
     * 
     * @component
     */
    private ArtifactResolver m_artifactResolver;
    
    


    public void execute() throws MojoExecutionException
    {
        ObrUpdate update;

        String mavenRepository = localRepository.getBasedir();

        URI repositoryXml = ObrUtils.findRepositoryXml( mavenRepository, obrRepository );
        URI obrXmlFile = ObrUtils.toFileURI( obrXml );

        Config userConfig = new Config();

        update = new ObrUpdate( repositoryXml, obrXmlFile, project, mavenRepository, userConfig, getLog() );
        update.parseRepositoryXml();
        
    	

    	/* Get URLs to the JAR files of all dependencies (also transitive ones) */

        DependencyNode dependencyTree;

        try
        {
            dependencyTree = m_dependencyTreeBuilder.buildDependencyTree( project, localRepository, m_factory,
                m_artifactMetadataSource, null, m_collector );
        }
        catch ( DependencyTreeBuilderException e )
        {
            throw new MojoExecutionException( "Unable to build dependency tree", e );
        }

        BundleInfo bundleInfo = new BundleInfo();

        getLog().debug( "Will bundle the following dependency tree" + LS + dependencyTree );

        for ( Iterator it = dependencyTree.inverseIterator(); it.hasNext(); )
        {
            DependencyNode node = ( DependencyNode ) it.next();
            if ( !it.hasNext() )
            {
                /* this is the root, current project */
                break;
            }

            if ( node.getState() != DependencyNode.INCLUDED )
            {
                continue;
            }

            if ( Artifact.SCOPE_SYSTEM.equals( node.getArtifact().getScope() ) )
            {
                getLog().debug( "Ignoring system scoped artifact " + node.getArtifact() );
                continue;
            }

            Artifact artifact;
            try
            {
                artifact = resolveArtifact( node.getArtifact() );
            }
            catch ( ArtifactNotFoundException e )
            {
                if ( ignoreMissingArtifacts )
                {
                    continue;
                }

                throw new MojoExecutionException( "Artifact was not found in the repo" + node.getArtifact(), e );
            }

            node.getArtifact().setFile( artifact.getFile() );

            int nodeDepth = node.getDepth();
            if ( nodeDepth > DEPTH )
            {
                /* node is deeper than we want */
                getLog().debug(
                    "Ignoring " + node.getArtifact() + ", depth is " + nodeDepth + ", bigger than " + DEPTH );
                continue;
            }
            
            /* Update OBR */
            
            File file = node.getArtifact().getFile();
            
            URI bundleJar;

            if ( null == file )
            {
                bundleJar = ObrUtils.getArtifactURI( localRepository, project.getArtifact() );
            }
            else
            {
                bundleJar = file.toURI();
                update.updateRepository( bundleJar, null, null );
            }

        }

        update.writeRepositoryXml();


    }
    

    private Artifact resolveArtifact( Artifact artifact ) throws MojoExecutionException, ArtifactNotFoundException
    {
        VersionRange versionRange;
        if ( artifact.getVersion() != null )
        {
            versionRange = VersionRange.createFromVersion( artifact.getVersion() );
        }
        else
        {
            versionRange = artifact.getVersionRange();
        }

        /*
         * there's a bug with ArtifactFactory#createDependencyArtifact(String, String, VersionRange,
         * String, String, String) that ignores the scope parameter, that's why we use the one with
         * the extra null parameter
         */
        Artifact resolvedArtifact = m_factory.createDependencyArtifact( artifact.getGroupId(),
            artifact.getArtifactId(), versionRange, artifact.getType(), artifact.getClassifier(), artifact.getScope(),
            null );

        try
        {
            m_artifactResolver.resolve( resolvedArtifact, remoteRepositories, localRepository );
        }
        catch ( ArtifactResolutionException e )
        {
            throw new MojoExecutionException( "Error resolving artifact " + resolvedArtifact, e );
        }

        return resolvedArtifact;
    }


}
